//#include "GLee.h"
#define GL_GLEXT_PROTOTYPES
#include "renderer.h"
#include "common.h"
#include <GL/glu.h>
#include <iostream>
#include <QMouseEvent>
#include <QTimer>
#include <QFile>
#include <vector>
#include <cmath>
#include <assert.h>
#include "treedraw.h"
#include "GL/glext.h"
#include "GL/glut.h"
#include "GL/gl.h"
#include "treerender.h"
#include <assert.h>
#include <stdio.h>
#include <QMessageBox>
#include "scene3d.h"


extern "C"{
    extern void APIENTRY glBindRenderbufferEXT (GLenum, GLuint);
    extern void APIENTRY glDeleteRenderbuffersEXT (GLsizei, const GLuint *);
    extern void APIENTRY glGenRenderbuffersEXT (GLsizei, GLuint *);
    extern void APIENTRY glActiveTexture (GLenum);
    extern void APIENTRY glFramebufferTexture2DEXT (GLenum, GLenum, GLenum, GLuint, GLint);
    extern void APIENTRY glRenderbufferStorageEXT (GLenum, GLenum, GLsizei, GLsizei);
    extern void APIENTRY glFramebufferRenderbufferEXT (GLenum, GLenum, GLenum, GLuint);
}
using namespace std;
//Constructor, camera gets initialized here
#define GL_GLEXT_PROTOTYPES
const float cos60 = cos(M_PI/3);
const float sin60 = sin(M_PI/3);
Renderer::Renderer(QWidget *parent) : QGLWidget(QGLFormat(QGL::SampleBuffers | QGL::DoubleBuffer),parent) , mFPS(30.0f) {

    mIncrement = 0;
    mCamera = new Camera();

    mCamera->eye.x = PLANE_SIZE/2.0, mCamera->eye.y = 30.0f, mCamera->eye.z = PLANE_SIZE/2.0f;
    mCamera->center.x = PLANE_SIZE/2.0, mCamera->center.y = 30.0f, mCamera->center.z = PLANE_SIZE/2.0+1.0f;
    mCamera->up.x = 0.0f, mCamera->up.y = 1.0f, mCamera->up.z = 0.0f;
    mCamera->_angle = 45.0f, mCamera->_near = 1.0f, mCamera->_far = 10000.0f;


    QObject::connect(this, SIGNAL(_mouseDrag(QMouseEvent*)), this, SLOT(mouseDragEvent(QMouseEvent*)));
    setAutoBufferSwap(false);
    setFocusPolicy(Qt::StrongFocus);
    mTimer = new QTimer(this);
    connect(mTimer, SIGNAL(timeout()), this, SLOT(redraw()));
    mTimer->start(1000.0f / mFPS);
    left = false;
    right = false;
    up = false;
    down = false;
    pan_up = false;
    pan_down = false;
    modeInteract = false;
    left_click = false;
    this->curIntersect = float3(0,0,0);
    mQuadric = gluNewQuadric();
    gluQuadricTexture(mQuadric, GL_TRUE);
    for (int row = 0; row < TILING_FACTOR; row++){
        for (int col = 0; col < TILING_FACTOR; col++){
            patchLocations[row * TILING_FACTOR + col].x = col*TILE_LENGTH+TILE_LENGTH/2;
            patchLocations[row * TILING_FACTOR + col].y = 0.0;
            patchLocations[row * TILING_FACTOR + col].z = row*TILE_LENGTH+TILE_LENGTH/2;
        }
    }


    m_scene = new Scene3D(this);

    for (int i = 0; i < RANDOM_GRASS; i++){
        float x = (rand()%10000)/10000.0 * PLANE_SIZE;
        float z = (rand()%10000)/10000.0 * PLANE_SIZE;
        this->random_grass_pos[i] = float2(x,z);
    }
    lambda = .58;
    tropism = .3;
    aux_angle = 3.14/2.0;
    shed_thres = .5;
    term_weight = 3;
    aux_weight = 3;
    diameter_rate = 2.8;
    tlength = 7;
    alength = 7;
    alpha = 2;
    grass_on = false;
    bark_mode = false;
}
//destructor
Renderer::~Renderer() {
    SAFE_DELETE(mTimer);
    SAFE_DELETE(mCamera);
    makeCurrent();
    gluDeleteQuadric(mQuadric);
    delete m_scene;
    foreach (QGLShaderProgram *sp, shader_programs)
        delete sp;
}

/**
    The main drawing method which will be called 30 frames per second.  Here
    is where your OpenGL drawing should occur.
**/

void Renderer::paintGL(){
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    time = mIncrement++ / (float)mFPS;
    updateInteraction();
    updateCamera();
    pickTree();

#ifdef ENVIRONMENT_VIEW
  /*  glActiveTexture(GL_TEXTURE3);
    glBindTexture( GL_TEXTURE_2D, depth_id);
    glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, framebuffer_objects["depth_map"] );
    glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, depth_id, 0 );
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                                 GL_RENDERBUFFER_EXT, renderBuffer);
    setCurrentShader("depth_map");
    renderScene(false);
    releaseShader("depth_map");
    glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0 );*/

    /*glActiveTexture(GL_TEXTURE4);
    glBindTexture( GL_TEXTURE_2D, color_id);
    glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, framebuffer_objects["color_map"] );

    glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, color_id, 0 );
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                                 GL_RENDERBUFFER_EXT, renderBuffer);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    renderScene(true);
    glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0 );*/
    //when going into framebuffer mode it does two things, fucks up alphas, fucks up interaction, fix that

    //glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );


    //TESTING PURPOSES
    /*glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,  color_id);
    setCurrentShader("basic_texture");
    shader_programs["basic_texture"]->setUniformValue("tex", GL_TEXTURE0);
    textured_quad(this->width(), this->height(), false);
    releaseShader("basic_texture");*/

    /*setCurrentShader("depth_of_field");
    textured_quad(this->width(), this->height(), false);
    releaseShader("depth_of_field");*/

#endif
    glClearColor(0.0,0.0,0.0,1.0);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 /*           glPushAttrib(GL_ALL_ATTRIB_BITS);
    setCurrentShader("barkTest");
     textured_quad_crop(256, 512, false);
     releaseShader("barkTest");
        glPopAttrib();*/


        glPushAttrib(GL_ALL_ATTRIB_BITS);
        renderScene(true);
        glPopAttrib();
        glPushAttrib(GL_ALL_ATTRIB_BITS);
        m_scene->draw(false);
        glPopAttrib();

   // testBark.drawNormals(testBarkStruct);

    swapBuffers();
    glFlush();

}

void Renderer::renderScene(bool render_sky){
    //DRAW UNDERLYING GRASS
    {
        float increment_factor = (float)PLANE_SIZE/(float)TILING_FACTOR;
        setCurrentShader("basic_texture");
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, this->grass_id);
        shader_programs["basic_texture"]->setUniformValue("tex", 0);
        for (int row = 0; row < TILING_FACTOR; row++){
            for (int col = 0; col < TILING_FACTOR;col++){
                glBegin(GL_QUADS);
                glTexCoord2d(0.0, 0.0);
                glVertex3f(col*increment_factor,0.0,(row+1)*increment_factor);

                glTexCoord2d(0.0, 1.0);
                glVertex3f((col+1)*increment_factor,0.0,(row+1)*increment_factor);

                glTexCoord2d(1.0, 1.0);
                glVertex3f((col+1)*increment_factor,0.0, (row)*increment_factor);

                glTexCoord2d(1.0, 0.0);
                glVertex3f(col*increment_factor,0.0, (row)*increment_factor);
                glEnd();
            }
        }
        releaseShader("basic_texture");
    }
    if (render_sky){
        setCurrentShader("basic_texture");
        renderSkybox(mSkybox);
        releaseShader("basic_texture");
    }


    //DRAW ACTUAL GRASS
    {
        if (grass_on){
            glDisable(GL_CULL_FACE);
            glEnable(GL_BLEND);
            glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
            glDisable(GL_LIGHTING);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glEnable(GL_TEXTURE_2D);

            setCurrentShader("grass_wave");
            int type = 0;
            shader_programs["grass_wave"]->setUniformValue("type", type);
            for (int row = 0; row < TILING_FACTOR; row++){
                for (int col = 0; col < TILING_FACTOR; col++){
                    float distance = (mCamera->eye - patchLocations[row*TILING_FACTOR+col]).getMagnitude2();
                    float xoff = GRASSW*cos(M_PI/3)+GRASSW-GRASSW/4+(int)distance/DENSITY_CONSTANT*GRASSW/10;
                    float yoff = GRASSW*sin(M_PI/3)-GRASSW/4+(int)distance/DENSITY_CONSTANT*GRASSW/10;
                    int width_elements = ceil(TILE_LENGTH/xoff);
                    int height_elements = ceil(TILE_LENGTH/yoff);
                    //render for current patch grass with certain density, try with regular density first
                    for (int i = 0; i < height_elements; i++){
                        for (int j = 0; j < width_elements; j++){
                            renderGrass(float2(col*TILE_LENGTH+j*xoff, row*TILE_LENGTH + i*yoff));
                            renderGrass(float2(col*TILE_LENGTH+j*xoff+xoff/2, row*TILE_LENGTH + i*yoff+yoff/2));
                        }
                    }
                }
            }
            type = 1;
            shader_programs["grass_wave"]->setUniformValue("type", type);
            for (int i = 0; i < RANDOM_GRASS; i++){
                renderGrass(this->random_grass_pos[i]);
            }
            releaseShader("grass_wave");
            glEnable(GL_LIGHTING);
            glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
            glDisable(GL_BLEND);
            glEnable(GL_CULL_FACE);
        }
    }
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, 0);
}



void Renderer::applyValues(){
    //int selected = m_scene->selected_index;

    for(int selected = 0; selected < m_scene->forest.size(); selected ++){
        m_scene->forest[selected]->LAMBDA = lambda;
        m_scene->forest[selected]->TROPISM = tropism;
        m_scene->forest[selected]->AUX_ANGLE = aux_angle;
        m_scene->forest[selected]->SHED_THRES = shed_thres;
        m_scene->forest[selected]->TERM_WEIGHT = term_weight;
        m_scene->forest[selected]->AUX_WEIGHT = aux_weight;
        m_scene->forest[selected]->DIAMETER_RATE = diameter_rate;
        m_scene->forest[selected]->TLENGTH = tlength;
        m_scene->forest[selected]->ALENGTH = alength;
        m_scene->forest[selected]->ALENGTH = alpha;
    }
}


/**
  Setup OpenGL here.  You should specify your render settings within this method.
  This method is called once when Qt first retreives the OpenGL context during the
  creation of this widget.
  **/

void Renderer::initializeGL() {
    glEnable(GL_POLYGON_SMOOTH); //Enable smoothin
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); //set material properties which will be assigned by glColor
    glCullFace (GL_BACK);
    glEnable (GL_CULL_FACE);
    glShadeModel(GL_SMOOTH); //Smooth or flat shading model
    glPolygonMode(GL_FRONT, GL_FILL); //Shaded mode
    glPolygonMode(GL_BACK, GL_FILL);
    glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);

    /* Setup Local Lighting *///Need a better lighting system
    GLfloat ambientLight[] = {0.1f, 0.1f, 0.1f, 1.0f};
    GLfloat diffuseLight[] = { 1.0f, 1.0f, 1.0, 1.0f };
    GLfloat specularLight[] = { 0.5f, 0.5f, 0.5f, 1.0f };

    GLfloat position[] = {250.0f, 250.0f, 100.0f, 1.0f };

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glClearColor(0.0,0.0, 0.0, 0.0); //Set the color to clear buffers to
    //glEnable(GL_LIGHTING); //Enable lighting
    glEnable(GL_LIGHT0);
    srand(3);
    glEnable(GL_TEXTURE_2D);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);


   palm_texture = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/palm_tree_bark_2.JPG"));
   grass_id = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/grass_texture.jpg"));
   soil_id = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/dirt.jpg"));
   grass_strip_id = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/grass_strip.jpg"));
   grass_distorted_id = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/distorted.jpg"));
   //TODO: find wrapping textures and meshes for leaves
   /*Setup skybox*/

  {
    mSkybox[0] = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/posx.jpg"));
    mSkybox[1] = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/negx.jpg"));
    mSkybox[2] = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/posy.jpg"));
    mSkybox[3] = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/negy.jpg"));
    mSkybox[4] = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/posz.jpg"));
    mSkybox[5] = loadTexture(QString::fromStdString("../cs224-self-organizing-trees/textures/negz.jpg"));
    QList<QFile *> fileList;
    fileList.append(new QFile("../cs224-self-organizing-trees/textures/posx.jpg"));
    fileList.append(new QFile("../cs224-self-organizing-trees/textures/negx.jpg"));
    fileList.append(new QFile("../cs224-self-organizing-trees/textures/posy.jpg"));
    fileList.append(new QFile("../cs224-self-organizing-trees/textures/negy.jpg"));
    fileList.append(new QFile("../cs224-self-organizing-trees/textures/negz.jpg"));
    fileList.append(new QFile("../cs224-self-organizing-trees/textures/posz.jpg"));

   if((mCubeID = loadCubeMap(fileList)) < 0) exit(0);
     glDisable(GL_TEXTURE_CUBE_MAP);

     foreach(QFile * f, fileList){
         delete f;
     }
 }
   glActiveTexture(GL_TEXTURE0);
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_TEXTURE_COORD_ARRAY);
   //glEnable(GL_TEXTURE_2D_ARRAY);

   glActiveTexture(GL_TEXTURE1);
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_TEXTURE_COORD_ARRAY);
   //glEnable(GL_TEXTURE_2D_ARRAY);

   glActiveTexture(GL_TEXTURE2);
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_TEXTURE_COORD_ARRAY);
   //glEnable(GL_TEXTURE_2D_ARRAY);

   glActiveTexture(GL_TEXTURE3);
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_TEXTURE_COORD_ARRAY);
   //glEnable(GL_TEXTURE_2D_ARRAY);

   glActiveTexture(GL_TEXTURE4);
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_TEXTURE_COORD_ARRAY);
   //glEnable(GL_TEXTURE_2D_ARRAY);

   glActiveTexture(GL_TEXTURE5);
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_TEXTURE_COORD_ARRAY);
   //glEnable(GL_TEXTURE_2D_ARRAY);

   glActiveTexture(GL_TEXTURE6);
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_TEXTURE_COORD_ARRAY);
   //glEnable(GL_TEXTURE_2D_ARRAY);
   glActiveTexture(GL_TEXTURE0);

   //for depth of field
   glGenTextures( 1, &color_id );
   glGenTextures( 1, &depth_id );



   m_scene->drawer->leaf = this->loadTexture("../cs224-self-organizing-trees/textures/leaf3D.png");
   m_scene->drawer->flower = this->loadTexture("../cs224-self-organizing-trees/textures/Petal.jpg");

   bark_common = this->loadTexture("../cs224-self-organizing-trees/textures/Common3.jpg");
   bark_lenticel = this->loadTexture("../cs224-self-organizing-trees/textures/Lenticel.jpg");
   bark_split = this->loadTexture("../cs224-self-organizing-trees/textures/Split2.jpg");

   loadShaders();
   load_framebuffers(this->width(), this->height());

   glGenTextures(1, &testBarkStruct.barkID);
   glGenTextures(1, &testBarkStruct.normalID);
   testBark.initBark(testBarkStruct);
  testBark.barkToTexture(testBarkStruct);
   updateCamera();
}

void Renderer::renderGrass(float2 position){

   // glActiveTexture(GL_TEXTURE0);
    glPushMatrix();
    glTranslatef(position.x, 0, position.y);
    glBegin(GL_QUADS);
    glMultiTexCoord2f(GL_TEXTURE0, 0.0, 1.0);
    glVertex3f(-GRASSW/2.0*cos60,GRASSH,GRASSW/2.0*sin60);
    glMultiTexCoord2f(GL_TEXTURE0,1.0, 1.0);
    glVertex3f(GRASSW/2.0*cos60,GRASSH,-GRASSW/2.0*sin60);
    glMultiTexCoord2f(GL_TEXTURE0,1.0, 0.0);
    glVertex3f(GRASSW/2.0*cos60,0.0f,-GRASSW/2.0*sin60);
    glMultiTexCoord2f(GL_TEXTURE0,0.0, 0.0);
    glVertex3f(-GRASSW/2.0*cos60,0.0f, GRASSW/2.0*sin60);
    glEnd();
    glPopMatrix();

    glPushMatrix();
    glTranslatef(position.x, 0, position.y);
    glBegin(GL_QUADS);
    glMultiTexCoord2f(GL_TEXTURE0,0.0, 1.0);
    glVertex3f(-GRASSW/2.0*cos60,GRASSH,-GRASSW/2.0*sin60);
    glMultiTexCoord2f(GL_TEXTURE0,1.0, 1.0);
    glVertex3f(GRASSW/2.0*cos60,GRASSH,GRASSW/2.0*sin60);
    glMultiTexCoord2f(GL_TEXTURE0,1.0, 0.0);
    glVertex3f(GRASSW/2.0*cos60,0.0f,GRASSW/2.0*sin60);
    glMultiTexCoord2f(GL_TEXTURE0,0.0, 0.0);
    glVertex3f(-GRASSW/2.0*cos60,0.0f, -GRASSW/2.0*sin60);
    glEnd();
    glPopMatrix();

    glPushMatrix();
    glTranslatef(position.x, 0, position.y);
    glBegin(GL_QUADS);
    glMultiTexCoord2f(GL_TEXTURE0,0.0, 1.0);
    glVertex3f(-GRASSW/2.0,GRASSH,0.0);
    glMultiTexCoord2f(GL_TEXTURE0,1.0, 1.0);
    glVertex3f(GRASSW/2.0,GRASSH,0.0);
    glMultiTexCoord2f(GL_TEXTURE0,1.0, 0.0);
    glVertex3f(GRASSW/2.0,0.0f,0.0);
    glMultiTexCoord2f(GL_TEXTURE0,0.0, 0.0);
    glVertex3f(-GRASSW/2.0,0.0f, 0.0);
    glEnd();
    glPopMatrix();


}

/**
  Shader loading methods
  **/
/**********************************************************************************/
void Renderer::loadShaders(){
    QString frag;
    QString vert;
    QString str;
    frag = "../cs224-self-organizing-trees/224_Trees/grass_wave.frag";
    vert = "../cs224-self-organizing-trees/224_Trees/grass_wave.vert";
    str = QString::fromStdString("grass_wave");
    shader_programs[str] = new QGLShaderProgram();
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Fragment, frag);
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Vertex, vert);
    linkShader(str);

    frag = "../cs224-self-organizing-trees/224_Trees/depth_of_field.frag";
    vert = "../cs224-self-organizing-trees/224_Trees/depth_of_field.vert";
    str = QString::fromStdString("depth_of_field");
    shader_programs[str] = new QGLShaderProgram();
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Fragment, frag);
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Vertex, vert);
    linkShader(str);

    frag = "../cs224-self-organizing-trees/224_Trees/depth_map.frag";
    vert = "../cs224-self-organizing-trees/224_Trees/depth_map.vert";
    str = QString::fromStdString("depth_map");
    shader_programs[str] = new QGLShaderProgram();
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Fragment, frag);
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Vertex, vert);
    linkShader(str);

    frag = "../cs224-self-organizing-trees/224_Trees/basic_texture.frag";
    vert = "../cs224-self-organizing-trees/224_Trees/basic_texture.vert";
    str = QString::fromStdString("basic_texture");
    shader_programs[str] = new QGLShaderProgram();
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Fragment, frag);
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Vertex, vert);
    linkShader(str);

    frag = "../cs224-self-organizing-trees/224_Trees/leafShader3D.frag";
    vert = "../cs224-self-organizing-trees/224_Trees/leafShader3D.vert";

    str = QString::fromStdString("color");
    shader_programs[str] = new QGLShaderProgram();
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Fragment, frag);
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Vertex, vert);
    linkShader(str);
    frag = "../cs224-self-organizing-trees/224_Trees/bark.frag";
    vert = "../cs224-self-organizing-trees/224_Trees/bark.vert";

    str = QString::fromStdString("bark");
    shader_programs[str] = new QGLShaderProgram();
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Fragment, frag);
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Vertex, vert);
    linkShader(str);

    frag = "../cs224-self-organizing-trees/224_Trees/barkTest.frag";
    vert = "../cs224-self-organizing-trees/224_Trees/barkTest.vert";

    str = QString::fromStdString("barkTest");
    shader_programs[str] = new QGLShaderProgram();
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Fragment, frag);
    shader_programs[str]->addShaderFromSourceFile(QGLShader::Vertex, vert);
    linkShader(str);


}
/*Set uniforms here if you need to*/
void Renderer::setCurrentShader(std::string shader){
    QString str = QString::fromStdString(shader);
     if (!shader.compare("grass_wave")){
         bindShader(str);
         glActiveTexture(GL_TEXTURE5);
         glBindTexture(GL_TEXTURE_2D, grass_strip_id);
        shader_programs["grass_wave"]->setUniformValue("grass", 5);

        glActiveTexture(GL_TEXTURE6);
        glBindTexture(GL_TEXTURE_2D, grass_distorted_id);
        shader_programs["grass_wave"]->setUniformValue("grass_dist", 6);

        shader_programs["grass_wave"]->setUniformValue("time", time);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, 0);
    }
     else if (!shader.compare("depth_of_field")){
         bindShader(str);

         glActiveTexture(GL_TEXTURE1);
         glBindTexture(GL_TEXTURE_2D, depth_id);
         shader_programs["depth_of_field"]->setUniformValue("depth_map", 1);
         glBindTexture(GL_TEXTURE_2D, color_id);
         shader_programs["depth_of_field"]->setUniformValue("color_map", 2);
     }
     else if (!shader.compare("depth_map")){
         bindShader(str);
         shader_programs["depth_map"]->setUniformValue("centX", mCamera->eye.x+mCamera->nLook.x*100);
         shader_programs["depth_map"]->setUniformValue("centY", mCamera->eye.y+mCamera->nLook.y*100);
         shader_programs["depth_map"]->setUniformValue("centZ", mCamera->eye.z+mCamera->nLook.z*100);
     }
     else if (!shader.compare("basic_texture")){
         bindShader(str);

     }
     else if (!shader.compare("bark")){
         if(bark_mode){
        bindShader(str);
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_2D, bark_common);
        shader_programs["bark"]->setUniformValue("common", 2);
        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D, bark_lenticel);
        shader_programs["bark"]->setUniformValue("lenticel", 3);
        glActiveTexture(GL_TEXTURE4);
        glBindTexture(GL_TEXTURE_2D, bark_split);
        shader_programs["bark"]->setUniformValue("split", 4);
        /*glActiveTexture(GL_TEXTURE5);
        glBindTexture(GL_TEXTURE_2D, testBarkStruct.normalID);
        shader_programs["bark"]->setUniformValue("normals", 5);
        glActiveTexture(GL_TEXTURE6);
        glBindTexture(GL_TEXTURE_2D, testBarkStruct.barkID);
        shader_programs["bark"]->setUniformValue("bark_info",6);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D,0);*/
        }
    }

     else if (!shader.compare("barkTest")){
         bindShader(str);
         glActiveTexture(GL_TEXTURE2);
         glBindTexture(GL_TEXTURE_2D, bark_common);
         shader_programs["bark"]->setUniformValue("common", 2);
         glActiveTexture(GL_TEXTURE3);
         glBindTexture(GL_TEXTURE_2D, bark_lenticel);
         shader_programs["bark"]->setUniformValue("lenticel", 3);
         glActiveTexture(GL_TEXTURE4);
         glBindTexture(GL_TEXTURE_2D, bark_split);
         shader_programs["bark"]->setUniformValue("split", 4);
         glActiveTexture(GL_TEXTURE5);
         glBindTexture(GL_TEXTURE_2D, testBarkStruct.normalID);
         shader_programs["bark"]->setUniformValue("normals", 5);
         glActiveTexture(GL_TEXTURE6);
         glBindTexture(GL_TEXTURE_2D, testBarkStruct.barkID);
         shader_programs["bark"]->setUniformValue("bark_info",6);
         glActiveTexture(GL_TEXTURE0);
         glBindTexture(GL_TEXTURE_2D,0);
     }
}

/**
  Framebuffer methods
  **/
/***********************************************************************************************/
void Renderer::load_framebuffers(int w, int h){
       glGenFramebuffersEXT(1, &framebuffer_objects["color_map"]);
        glBindTexture( GL_TEXTURE_2D, color_id);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0,
                     GL_RGBA, GL_UNSIGNED_BYTE, 0);
        glBindTexture(GL_TEXTURE_2D, 0);


       glGenFramebuffersEXT(1, &framebuffer_objects["depth_map"]);
        glBindTexture( GL_TEXTURE_2D, depth_id );
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0,
                     GL_RGBA, GL_UNSIGNED_BYTE, 0);
        glBindTexture(GL_TEXTURE_2D, 0);



        glGenRenderbuffersEXT(1, &renderBuffer);
        glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderBuffer);
        glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h);
        glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderBuffer);
  }


/**
  Texturing methods
  **/
/**********************************************************************************************************/
/*loads a texture with the name filename*/
GLuint Renderer::loadTexture(const QString &filename) {
    QImage image, texture;
    QFile file(filename);
    GLuint toReturn;
    if(!file.exists()){
        std::cout << "Failed to load texture: " << file.fileName().toStdString() << std::endl;
        return -1;
    }
    image.load(file.fileName());
   // image = image.mirrored(true, false);col
    texture = QGLWidget::convertToGLFormat(image);

    glGenTextures(1, &toReturn);
    glBindTexture(GL_TEXTURE_2D, toReturn);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, texture.width(), texture.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, texture.width(), texture.height(), GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    return toReturn;

}

/*Loads a cube map with all 6 textured sides*/
GLuint Renderer::loadCubeMap(QList<QFile *> files) {
    int i;
    GLuint id = -1;

    glEnable(GL_DEPTH_TEST);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

    for(i = 0; i < 6; ++i) {
        QImage image, texture;
        image.load(files[i]->fileName());
        if (i != 2 && i != 3){
          image = image.mirrored(true, true);
       }
        texture = QGLWidget::convertToGLFormat(image);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 3, texture.width(), texture.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
    }

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
    glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);

    glEnable(GL_TEXTURE_CUBE_MAP);
    glEnable(GL_NORMALIZE);

    return id;
}

/*Renderes the cube map*/
void Renderer::renderSkybox(GLuint mSkybox[6]) {

    // Push a new matrix onto the stack for modelling transformations
    glPushMatrix();
    // Reset and transform the matrix.
     glTranslatef(PLANE_SIZE/2, 50, PLANE_SIZE/2);

    glScalef(PLANE_SIZE+1, PLANE_SIZE, PLANE_SIZE+1);
   // glTranslatef(mCamera->eye.x, mCamera->eye.y, mCamera->eye.z);
    glEnable(GL_TEXTURE_2D);
   // glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);
    glDisable(GL_BLEND);
    // Just in case we set all vertices to white.
    glColor4f(1,1,1,1.0);

    // Render the right quad
    glActiveTexture(GL_TEXTURE0);
   glBindTexture(GL_TEXTURE_2D, mSkybox[0]);
   shader_programs["basic_texture"]->setUniformValue("tex", 0);
    glBegin(GL_QUADS);
    glMultiTexCoord2f(GL_TEXTURE0,1, 0); glVertex3f( 0.5f, -0.5f,  0.5f );
    glTexCoord2f(1, 1); glVertex3f( 0.5f,  0.5f,  0.5f );
    glTexCoord2f(0, 1); glVertex3f( 0.5f,  0.5f, -0.5f );
    glTexCoord2f(0, 0); glVertex3f( 0.5f, -0.5f, -0.5f );
    glEnd();

    // Render the left quad
    glBindTexture(GL_TEXTURE_2D, mSkybox[1]);
       shader_programs["basic_texture"]->setUniformValue("tex", 0);
    glBegin(GL_QUADS);
    glTexCoord2f(0, 0); glVertex3f(  -0.5f, -0.5f,  0.5f );
    glTexCoord2f(1, 0); glVertex3f(  -0.5f, -0.5f, -0.5f );
    glTexCoord2f(1, 1); glVertex3f(  -0.5f,  0.5f, -0.5f );
    glTexCoord2f(0, 1); glVertex3f(  -0.5f,  0.5f,  0.5f );
    glEnd();

    // Render the top quad
    glBindTexture(GL_TEXTURE_2D, mSkybox[2]);
       shader_programs["basic_texture"]->setUniformValue("tex", 0);
    glBegin(GL_QUADS);
        glTexCoord2f(1, 0); glVertex3f(  0.5f,  0.5f, -0.5f );
        glTexCoord2f(1, 1); glVertex3f(  0.5f,  0.5f,  0.5f );
        glTexCoord2f(0, 1); glVertex3f( -0.5f,  0.5f,  0.5f );
        glTexCoord2f(0, 0); glVertex3f( -0.5f,  0.5f, -0.5f );
    glEnd();

    // Render the bottom quad
       glBindTexture(GL_TEXTURE_2D, mSkybox[3]);
          shader_programs["basic_texture"]->setUniformValue("tex", 0);
    glBegin(GL_QUADS);
        glTexCoord2f(0, 1); glVertex3f( -0.5f, -0.5f, -0.5f );
        glTexCoord2f(0, 0); glVertex3f( -0.5f, -0.5f,  0.5f );
        glTexCoord2f(1, 0); glVertex3f(  0.5f, -0.5f,  0.5f );
        glTexCoord2f(1, 1); glVertex3f(  0.5f, -0.5f, -0.5f );
    glEnd();

    // Render the back quad
    glBindTexture(GL_TEXTURE_2D, mSkybox[4]);
       shader_programs["basic_texture"]->setUniformValue("tex", 0);
    glBegin(GL_QUADS);
        glTexCoord2f(0, 0); glVertex3f( -0.5f, -0.5f,  -0.5f );
    glTexCoord2f(1, 0); glVertex3f(  0.5f, -0.5f,  -0.5f );
    glTexCoord2f(1, 1); glVertex3f(  0.5f,  0.5f,  -0.5f );
        glTexCoord2f(0, 1); glVertex3f( -0.5f,  0.5f,  -0.5f );
    glEnd();

    //Render the front quad
    glBindTexture(GL_TEXTURE_2D, mSkybox[5]);
       shader_programs["basic_texture"]->setUniformValue("tex", 0);
    glBegin(GL_QUADS);
        glTexCoord2f(0, 0); glVertex3f(  0.5f, -0.5f, 0.5f );
    glTexCoord2f(1, 0); glVertex3f( -0.5f, -0.5f, 0.5f );
    glTexCoord2f(1, 1); glVertex3f( -0.5f,  0.5f, 0.5f );
    glTexCoord2f(0, 1); glVertex3f(  0.5f,  0.5f, 0.5f );
    glEnd();



    glPopMatrix();
    glEnable(GL_LIGHTING);
    //glEnable(GL_BLEND);
}


/**
  Methods for rendering to a textured quad
  **/
/****************************************************************************************************************/
/*Render a textured quad*/
void Renderer::textured_quad(int w,int h,bool flip) {
   /* glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, this->width(), 0, this->height(),-100.f,100.f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();*/

    glBegin(GL_QUADS);
    glTexCoord2f(0.0f,flip ? 1.0f : 0.0f);
    glVertex3f(0.0f,0.0f,0.0);
    glTexCoord2f(1.0f,flip ? 1.0f : 0.0f);
    glVertex3f(w,0.0f,0.0);
    glTexCoord2f(1.0f,flip ? 0.0f : 1.0f);
    glVertex3f(w,h,0.0);
    glTexCoord2f(0.0f,flip ? 0.0f : 1.0f);
    glVertex3f(0.0f,h,0.0);
    glEnd();
}

void Renderer::textured_quad_crop(int w,int h, bool flip) {
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f,flip ? 1.0f : 0.0f);
    glVertex3f(0.0f,0.0f,0.0);
    glTexCoord2f(1.0f,flip ? 1.0f : 0.0f);
    glVertex3f(w,0.0f,0.0);
    glTexCoord2f(1.0f,flip ? 0.0f : 1.0f);
    glVertex3f(w,h,0.0);
    glTexCoord2f(0.0f,flip ? 0.0f : 1.0f);
    glVertex3f(0.0f,h,0.0);
    glEnd();

}


/*Convert camera to orthographic mode*/
void Renderer::orthoMode(int w, int h){
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0,static_cast<float>(w),static_cast<float>(h),0.f,-1.f,1.f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/**
  User interaction, update methods...
  **/
/****************************************************************************************************************/

   /* Resets the camera to fit the new viewport when the user resizes
    the window.  (Avoids object distortion)*/
void Renderer::resizeGL(int w, int h) {
    // Prevent a divide by zero, when window is too short
    // (you cant make a window of zero width).
    if(h == 0) h = 1;
    // Set the viewport to be the entire window (you probably want to keep this)
    glViewport(0, 0, w, h);
    updateCamera();
   // realloc_framebuffers(w, h);
}
void Renderer::realloc_framebuffers(int w, int h) {
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, framebuffer_objects["fbo_0"]);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h);
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, framebuffer_objects["fbo_0"]);
     glBindTexture( GL_TEXTURE_2D, framebuffer_objects["color_map"] );

    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB,
                      w, h,
                      0, GL_RGB, GL_UNSIGNED_BYTE, 0 );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );

     glBindTexture( GL_TEXTURE_2D, framebuffer_objects["depth_map"] );
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB,
                      w, h,
                      0, GL_RGB, GL_UNSIGNED_BYTE, 0 );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
}

void Renderer::updateCamera() {
    float w = width(), h = height();
    float ratio = 1.0f * w / h;
    // Reset the coordinate system before modifying
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(mCamera->_angle, ratio, mCamera->_near, mCamera->_far);
    mCamera->nLook  = (mCamera->center - mCamera->eye).getNormalized();
    float3 tempup = mCamera->up.getNormalized();
    mCamera->nCross = -mCamera->nLook.cross(tempup).getNormalized();
    mCamera->nUp = mCamera->nLook.cross(mCamera->nCross).getNormalized();
    gluLookAt(mCamera->eye.x, mCamera->eye.y, mCamera->eye.z,
              mCamera->center.x, mCamera->center.y, mCamera->center.z,
              mCamera->up.x, mCamera->up.y, mCamera->up.z);
    glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

void Renderer::mousePressEvent(QMouseEvent *event) {
    mLastMousePos.x = event->x(); mLastMousePos.y = event->y();
    if(event->button() == Qt::RightButton) { //Roghtclick
        //This finds the world space coordinates of you mouse click
        //assuming the film plane is at z=1
        GLint viewport[4];
        GLdouble mvmatrix[16], projmatrix[16];
        GLint realy; /* OpenGL y coordinate position */
        GLdouble wx, wy, wz; /* returned world x, y, z coords     */
        glGetIntegerv (GL_VIEWPORT, viewport);
        glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
        glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
        realy = viewport[3] - (GLint) event->y() - 1;
        gluUnProject ((GLdouble) event->x(), (GLdouble) realy, 1.0,
                      mvmatrix, projmatrix, viewport, &wx, &wy, &wz);

        float3 ray = (float3(wx, wy, wz)-mCamera->eye).getNormalized();
        //noprmal is 0 1 0
        double d = -mCamera->eye.y/ray.y;

        this->curIntersect = mCamera->eye+d*ray;
        if (d > 0 && curIntersect.x >= 0 && curIntersect.x <= PLANE_SIZE && curIntersect.z >= 0 && curIntersect.z <= PLANE_SIZE && plant){
            this->m_scene->addTree(float2(curIntersect.x, curIntersect.z));
            this->applyValues();
            this->iterate();
            cout << "PLANTED A TREE " << endl;
        }
    } else if (event->button() == Qt::LeftButton) { //left click



    } else if (event->button() == Qt::MidButton) { //Mid click

    }
}

void Renderer::mouseReleaseEvent(QMouseEvent *event){
    if(event->button() == Qt::LeftButton && event->x() >= 0 && event->x() < width() && event->y() >= 0 && event->y() < height()){
      left_click= true;
      click_up_pos.x = event->x();
      click_up_pos.y = event->y();
    }
}

void Renderer::updateBark(){
    //testBark.ageBark(testBarkStruct);
    //testBark.barkToTexture(testBarkStruct);
    m_scene->updateSelectedTreeBark();
}

void Renderer::mouseMoveEvent(QMouseEvent *event) {
    if(event->buttons() & Qt::LeftButton || event->buttons() & Qt::RightButton) //Mouse drag - don't modify this
        emit _mouseDrag(event);
    else if (event->buttons() & Qt::NoButton) {
    }
    mLastMousePos.x = event->x(); mLastMousePos.y = event->y();
}

void Renderer::mouseDragEvent(QMouseEvent *event) {
    if(event->buttons() & Qt::LeftButton) {
          if (!modeInteract){
             float dx = event->x() - mLastMousePos.x;
             float dy = event->y() - mLastMousePos.y;
             float3 look = mCamera->center - mCamera->eye;
             double r = look.getMagnitude();

             fflush(stdout);
             double theta = acos(look.y / r) + dy / 500.0f;
             double phi = atan2(look.z, look.x) + dx / 500.0f;
             if (theta > M_PI-.1) theta =M_PI-.1;
             if (theta < .1) theta = .1;

             float3 tempCenter;
             tempCenter.x = r * sin(theta) * cos(phi) + mCamera->eye.x;
             tempCenter.z = r * sin(theta) * sin(phi) + mCamera->eye.z;
             tempCenter.y = r * cos(theta) + mCamera->eye.y;
             look = (tempCenter - mCamera->eye).getNormalized();
             float3 axis(0,-1,0);
#ifdef LIMIT_VIEW
             if (look.dot(axis) <= LOOK_LIMIT){
                mCamera->center = tempCenter;
             }
             else{
                 theta -= dy / 500.0f;
                 mCamera->center.x = r * sin(theta) * cos(phi) + mCamera->eye.x;
                 mCamera->center.z = r * sin(theta) * sin(phi) + mCamera->eye.z;
                 mCamera->center.y = r * cos(theta) + mCamera->eye.y;
             }
#else
         mCamera->center = tempCenter;
#endif
         }
          else{
              //perform Interaction here
          }
     }
}

void Renderer::wheelEvent(QWheelEvent *event) {

}

void Renderer::keyPressEvent(QKeyEvent *event) {

    if(event->key() == Qt::Key_Q) {
        pan_up = true;
    }
    else if (event->key() == Qt::Key_E){
        pan_down = true;
    }
    else if (event->key() == Qt::Key_W) {
        up = true;
    }
    else if (event->key() == Qt::Key_A) {
        left = true;
    }
    else if (event->key() == Qt::Key_D) {
        right = true;
    }
    else if (event->key() == Qt::Key_S) {
        down = true;
    }
    else if (event->key() == Qt::Key_Shift){
        plant = true;
    }
    else if (event->key() == Qt::Key_L){
        testBark.ageBark(testBarkStruct);
        testBark.barkToTexture(testBarkStruct);
    }
    else if (event->key() == Qt::Key_Up){
        forward = true;
    }
    else if (event->key() == Qt::Key_Down){
        backward = true;
    }


}

void Renderer::keyReleaseEvent(QKeyEvent *event) {

    if(event->key() == Qt::Key_Q) {
        pan_up = false;
    }
    else if(event->key() == Qt::Key_E){
        pan_down= false;
    }
    else if (event->key() == Qt::Key_W) {
        up = false;
    }
    else if (event->key() == Qt::Key_A) {
        left = false;
    }
    else if (event->key() == Qt::Key_D) {
        right = false;
    }
    else if (event->key() == Qt::Key_S) {
        down = false;
    }
    else if (event->key() == Qt::Key_Shift){
        plant = false;
    }
    else if (event->key() == Qt::Key_Up){
        forward = false;
    }
    else if (event->key() == Qt::Key_Down){
        backward = false;
    }
}
void Renderer::pickTree(){
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_LIGHTING);
    glClearColor(1.0, 1.0,1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    m_scene->draw(true);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_LIGHTING);

    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT,viewport);
    if (left_click){
        GLubyte pixel[3];
        glReadPixels(click_up_pos.x, viewport[3]-click_up_pos.y, 1,1, GL_RGB, GL_UNSIGNED_BYTE, (void*)pixel);
        cout << (int)pixel[0] << "  " << (int)pixel[1] << "  " << (int)pixel[2] << endl;
        if (pixel[0]!=255 && pixel[0] < m_scene->forest.size() && pixel[1]*255+pixel[2] < m_scene->forest[pixel[0]]->branchIdGen){
            m_scene->selected_tree = pixel[0];
            int selected_branch = pixel[1]*255+pixel[2];
            Bud *selected_bud = ((TreeGen3D*)(m_scene->forest[m_scene->selected_tree]))->findBud(selected_branch);
            if (selected_bud){
              if (plant && m_scene->selected_tree != -1 && selected_branch != -1 && selected_bud != NULL){
                    if (selected_bud == m_scene->forest[m_scene->selected_tree]->treeRoot){
                        m_scene->clear();
                    }
                    else if (selected_bud->parent->aux == selected_bud){
                        Bud *parent = selected_bud->parent;
                        m_scene->forest[m_scene->selected_tree]->freeTree(selected_bud);
                        parent->aux = NULL;
                        delete m_scene->forest[m_scene->selected_tree]->m_mesh;
                        m_scene->forest[m_scene->selected_tree]->m_mesh = NULL;
                        m_scene->forest[m_scene->selected_tree]->m_mesh = new TreeMesh(m_scene->forest[m_scene->selected_tree], this);
                    }
                    else if (selected_bud->parent->term == selected_bud){
                        Bud *parent = selected_bud->parent;
                        m_scene->forest[m_scene->selected_tree]->freeTree(selected_bud);
                        parent->term = NULL;
                        delete m_scene->forest[m_scene->selected_tree]->m_mesh;
                        m_scene->forest[m_scene->selected_tree]->m_mesh = NULL;
                        m_scene->forest[m_scene->selected_tree]->m_mesh = new TreeMesh(m_scene->forest[m_scene->selected_tree], this);
                    }
                    else{
                     assert(0);
                    }

            }
            }
       }
        else{
            m_scene->selected_tree = -1;
        }
        left_click = false;
    }
}
void Renderer::updateInteraction(){

    if (up) {
       mCamera->eye += 2*float3(mCamera->nLook.x, 0.0, mCamera->nLook.z);
       mCamera->center += 2*float3(mCamera->nLook.x, 0.0, mCamera->nLook.z);
   }
    if (left) {
       mCamera->eye += 2*float3(mCamera->nCross.x,0.0, mCamera->nCross.z);
       mCamera->center += 2*float3(mCamera->nCross.x,0.0, mCamera->nCross.z);
    }
    if (right) {
       mCamera->eye -= 2*float3(mCamera->nCross.x,0.0, mCamera->nCross.z);
       mCamera->center -= 2*float3(mCamera->nCross.x,0.0, mCamera->nCross.z);
    }
    if (down) {
       mCamera->eye -= 2*float3(mCamera->nLook.x, 0.0, mCamera->nLook.z);
       mCamera->center -= 2*float3(mCamera->nLook.x, 0.0, mCamera->nLook.z);
    }
    if (pan_up){
#ifdef LIMIT_VIEW
        if (mCamera->eye.y + mCamera->nUp.y <= VERT_LIMIT_T){
            mCamera->eye += mCamera->nUp;
            mCamera->center += mCamera->nUp;
        }
#else
        mCamera->eye += mCamera->nUp;
        mCamera->center += mCamera->nUp;
#endif
    }
    if(pan_down){
#ifdef LIMIT_VIEW
        if (mCamera->eye.y-mCamera->nUp.y >= VERT_LIMIT_B){
            mCamera->eye -= mCamera->nUp;
            mCamera->center -= mCamera->nUp;
        }
#else
        mCamera->eye -= mCamera->nUp;
        mCamera->center -= mCamera->nUp;
#endif
    }
    if(forward){
        mCamera->eye += mCamera->nLook * 10.f;
        mCamera->center += mCamera->nLook * 10.f;
    }
    if(backward){
        mCamera->eye -= mCamera->nLook * 10.f;
        mCamera->center -= mCamera->nLook * 10.f;
    }
}

void Renderer::redraw() {
    repaint();
}

void Renderer::iterate(){
    this->m_scene->iterate();
}

void Renderer::clear(){
    m_scene->clear();
}

bool Renderer::saveScene(QString filename)
{
    //return m_scene->saveScene(filename,this->mCamera);
    return m_scene->bsave(filename, this->mCamera);
}

bool Renderer::loadScene(QString filename)
{
    //bool res = m_scene->loadScene(filename,this->mCamera);
    bool res = m_scene->bload(filename,this->mCamera);
    if (res == true)
        updateCamera();
    return res;
}
